Developer Documentation

QuickTime 4 API Documentation

QuickTime 4 Reference

| Previous | Chapter Contents | Chapter Top | Next |

The EffectBegin function

The main tasks that the EffectBegin function should perform are:

Checking Source and Destination References

The following code checks to see if the destination has changed since the last call to the EffectBegin function:

if (p->conditionFlags & (codecConditionNewClut+
                    codecConditionFirstFrame+codecConditionNewDepth+

    codecConditionNewDestination+codecConditionNewTransform))

If this evaluates to true , the destination has changed. This expression checks a series of flags that are passed to the EffectBegin function in the conditionsFlags field of the decompressParams parameter. When the destination is changed, QuickTime sets these flags to alert the effect component to update its internal state.

The most important information that you need to store about the new destination is its base address and its rowBytes value. These values allow you to draw onto the destination surface.

Listing 17 shows an example function that stores information in the effect component's global data structure about the destination PixMap passed to the function.

Listing 17 Storing information about a new destination frame

static long BlitterSetDest(BlitGlobals*glob,    // input: our globals
    PixMap  *dstPixMap,     // input: pixels we will draw into
    Rect    *dstRect)       // input: area of pixels we will draw into
{
    OSErr   result = noErr;
    long    offsetH,offsetV;
    char    *baseAddr;

    // Calculate the based address according to the format of the
    // destination PixMap
    offsetH = (dstRect->left - dstPixMap->bounds.left);
    if (dstPixMap->pixelSize == 16)
    {
        offsetH <<= 1;          // 1 pixel = 2 bytes
    }
    else
    {
        if (dstPixMap->pixelSize == 32)
        {
            offsetH <<= 2;      // 1 pixel = 4 bytes
        }
        else
        {
            result = -1;        // this is a data format we can't handle
        }
    }
    offsetV = (dstRect->top - dstPixMap->bounds.top)
                * dstPixMap->rowBytes;
    baseAddr = dstPixMap->baseAddr + offsetH + offsetV;

    glob->dstBaseAddr = baseAddr;
    glob->dstRowBytes = dstPixMap->rowBytes;

    return result;
} // BlitterSetDest

The process for checking for new sources is broadly similar. The CodecDecompressParams data structure passed into the EffectBegin function has a field called majorSourceChangeSeed . This contains a seed number generated from the characteristics of the set of sources for the effect. If the sources change, the majorSourceChangeSeed value will also change, so the effect can store the current value in its global data structure and compare it to the current value. If they are different, the effect knows its sources have changed.

When the effect detects that one or more of its sources have changed, it must iterate through all its sources and reload information about them.

Listing 18 shows example code that performs these operations. Listing 19 shows the BlitterSetSource function that is called by this example code. The BlitterSetSource function is analogous to the BlitterSetDest function shown in Listing 17 .

Listing 18 Checking for source changes

// Check to see if one or more sources have changed
if (p->majorSourceChangeSeed != glob->majorSourceChangeSeed)
{
    // grab start of input chain for this effect
    source = effect->source;

    // we can play with up to kMaxSources sources, so go get them
    while (source != nil && numSources < kMaxSources)
    {
        // now give that source to our blitter
        err = BlitterSetSource(glob, numSources, source);
        if (err != noErr)
            goto bail;

        source = source->next;
        ++numSources;
    }
}

Listing 19 Storing information about a new source frame

static long BlitterSetSource(BlitGlobals*glob,      // input: our globals
    long sourceNumber,                  // input: source index to set
    CDSequenceDataSourcePtr source)     // input: source value
{
    OSErr   err = noErr;

    if (sourceNumber >= kMaxSources)
    {
        // too many sources for us to handle
        return noErr;
    }
    else
    {
        // a source we can handle, save it away
        err = RequestImageFormat(source, glob->width, glob->height,
                                    glob->dstPixelFormat);
        if (err == noErr)
        {
            glob->sources[sourceNumber].src = source;
        }
        else
        {
            glob->sources[sourceNumber].src = nil;
        }
    }
    return (err);

} // BlitterSetSource

Reading Parameter Values

Listing 20 shows how to read the value of a non-tweened parameter. The QTFindChildByID function is used to retrieve the atom containing the parameter value. The parameter value is then copied from the atom using the function QTCopyAtomDataToPtr . If the value is successfully copied, it is endian-flipped to ensure it is in native-endian format (parameter values are always stored in big-endian format). If the copy failed, a default value is provided.

The value retrieved from the parameter is stored in the component's global data structure (called, in this example, global->blitter ). This allows the value to be used by other functions, notably the component's EffectRenderFrame function.

Listing 20 Reading a parameter value

{
    Ptr             data = p->data;
    QTAtom          atom;
    QTAtomID        atomID = 1;
    long            actSize;

    // Find the 'sden' atom
    atom = QTFindChildByID((QTAtomContainer) &data,
                        kParentAtomIsContainer,
                        OSTypeConst('sden'), // The name of the parameter
                        atomID,              // The ID of the parameter
                        nil);

    // Copy the parameter value from the atom
    if (QTCopyAtomDataToPtr((QTAtomContainer) &data,
                        atom,
                        false,
                        sizeof(long),
                        &((glob->blitter).scratchDensity),
                        &actSize)!=noErr)
    {
        // If the copy failed, use a default value for this parameter
        ((glob->blitter).scratchDensity) = 1;
    }
    else
    {
        // Otherwise, the copy succeeded, so endian flip and store the
        // parameter value
        ((glob->blitter).scratchDensity) = EndianS32_BtoN(((glob->blitter).scratchDensity));
    }
}

If the parameter value can contain a tweened value, you can use code similar to that shown in Listing 21 to retrieve the parameter value. The functions InitializeTweenGlobals and CreateTweenRecord are utility functions that Apple provides as part of the dimmer effect sample framework (see "The Sample Effect Component" ).

Listing 21 Reading a tweened parameter value

{
    Ptr                 data = p->data;
    OSErr               err;
    long                index = 1;

    err = InitializeTweenGlobals(&glob->tweenGlobals, p);
    if (err!=noErr)
        goto bail;

    // Make our tweener, return if we already have it
    err = CreateTweenRecord(&glob->tweenGlobals,
                    &glob->percentage,
                    OSTypeConst('pcnt'),    // The name of the parameter
                    1,                      // The ID of the parameter
                    sizeof(Fixed),
                    kTweenTypeFixed,
                    (void*) 0,
                    (void*) fixed1,
                    effect->frameTime.virtualDuration);
    if (err!=noErr)
        goto bail;

    glob->initialized = true;
}

Tweening Parameter Values

If you have specified that one or more of your parameter's values can be tweened, you need to implement code to perform the tweening in the EffectBegin function.

Listing 22 shows an example of tweening a parameter value. The current frame time is retrieved and subtracted from the effect's virtualStartTime . This calculates how far through the execution of the current effect sequence we are, expressed as a percentage.

With this information, the code then calls QTDoTween to interpolate the parameter value, leaving the resulting value in glob->comp1Tween.tweenData .

Listing 22 Tweening parameter values

wide    percentage;

// Find out how far through the effect we are
percentage = effect->frameTime.value;
CompSub(&effect->frameTime.virtualStartTime, &percentage);

// Tween our parameters and get the current value for this frame, prepare
// to render it when the EffectRenderFrame happens
{
    Fixed   thePercentage;

    if (glob->percentage.tween)
        QTDoTween(glob->percentage.tween, percentage.lo,
                    glob->percentage.tweenData, nil, nil, nil);

    thePercentage = **(Fixed**) (glob->percentage.tweenData);

    // If we are before the half-way point of this transition, we should
    // be fading the first source to black
    if (thePercentage < fixed1/2)
    {
        (glob->blitter).direction = 1;
        (glob->blitter).dimValue = FixedToInt(FixMul(IntToFixed(512), thePercentage));
    }
    // Otherwise, we are fading up onto the new source
    else
    {
        (glob->blitter).direction = 0;
        (glob->blitter).dimValue = FixedToInt(FixMul(IntToFixed(512),
                                                thePercentage)) - 255;
    }
}

© 1999 Apple Computer, Inc.

| Previous | Chapter Contents | Chapter Top | Next |